package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.to.mq.StockDetailTo;
import com.atguigu.common.to.mq.StockLockedTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.vo.OrderItemVo;
import com.atguigu.gulimall.ware.vo.OrderVo;
import com.atguigu.gulimall.ware.vo.SkuHasStockVo;
import com.atguigu.gulimall.ware.vo.WareSkuLockTo;
import com.atguigu.gulimall.ware.exception.NoStockException;

import lombok.Data;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;
    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderFeignService orderFeignService;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId)){
            queryWrapper.eq("sku_id",skuId);
        }
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)){
            queryWrapper.eq("ware_id",wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //1、判断如果还没有这个库存记录新增
        List<WareSkuEntity> entities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if(entities == null || entities.size() == 0){
            WareSkuEntity skuEntity = new WareSkuEntity();
            skuEntity.setSkuId(skuId);
            skuEntity.setStock(skuNum);
            skuEntity.setWareId(wareId);
            skuEntity.setStockLocked(0);
            //TODO 远程查询sku的名字，如果失败，整个事务无需回滚
            //1、自己catch异常
            //TODO 还可以用什么办法让异常出现以后不回滚？高级
            try {

                R info = productFeignService.info(skuId);
                Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");

                if(info.getCode() == 0){
                    skuEntity.setSkuName((String) data.get("skuName"));
                }
            }catch (Exception e){

            }


            wareSkuDao.insert(skuEntity);
        }else{
            wareSkuDao.addStock(skuId,wareId,skuNum);
        }

    }

    @Override
    public List<SkuHasStockVo> getSkuHasStok(List<Long> skuIds) {
        List<SkuHasStockVo> skuHasStockVoList = skuIds.stream().map(skuId -> {
            SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
            Long stock = this.baseMapper.getSkuStock(skuId);
            skuHasStockVo.setSkuId(skuId);
            skuHasStockVo.setHasStock(stock == null ? false : stock > 0);
            return skuHasStockVo;
        }).collect(Collectors.toList());
        return skuHasStockVoList;
    }

    @Transactional
    @Override
    public boolean orderLockStock(WareSkuLockTo skuLockTo) {
        //保存库存工作单详情
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(skuLockTo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

        List<OrderItemVo> locks = skuLockTo.getLocks();
        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            skuWareHasStock.setSkuId(skuId);
            skuWareHasStock.setNum(item.getCount());
            List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId);
            skuWareHasStock.setWareId(wareIds);
            return skuWareHasStock;
        }).collect(Collectors.toList());
        //锁定库存
        boolean allIsLock = true;
        for (SkuWareHasStock hasStock : collect) {
            Long skuId = hasStock.getSkuId();
            Integer num = hasStock.getNum();
            List<Long> wareIds = hasStock.getWareId();
            if (wareIds!=null&& wareIds.size()>0){
                boolean skuIsLock = false;
                for (Long wareId : wareIds) {
                    int count = wareSkuDao.lockSkuStock(skuId,wareId,num);
                    if (count == 1){
                        skuIsLock = true;
                        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null, skuId, null, num, wareOrderTaskEntity.getId(), wareId, 1);
                        wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                        StockLockedTo stockLockedTo = new StockLockedTo();
                        stockLockedTo.setId(wareOrderTaskEntity.getId());
                        StockDetailTo stockDetailTo = new StockDetailTo();
                        BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockDetailTo);
                        stockLockedTo.setDetail(stockDetailTo);
                        rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo);
                        break;
                    }
                }
                if (!skuIsLock){
                    allIsLock = false;
                    throw new NoStockException(skuId);
                }
            }else {
                throw new NoStockException(skuId);
            }
        }
        return allIsLock;
    }


    @Override
    public void unlockStock(StockLockedTo to){
        StockDetailTo detail = to.getDetail();
        Long detailId = detail.getId();
        //这里业务依赖主键自增有些问题  mysql 5.7以下中存在主键自增 回滚 然后重启后自增主键丢失
        // 8.0以后 通过将自增id持久化到磁盘中解决了
        WareOrderTaskDetailEntity taskDetail = wareOrderTaskDetailService.getById(detailId);
        if (taskDetail!=null&&taskDetail.getLockStatus()==1){
            //查看订单状态
            Long id = to.getId();
            WareOrderTaskEntity wareOrderTask = wareOrderTaskService.getById(id);
            String orderSn = wareOrderTask.getOrderSn();
            R r = orderFeignService.getOrder(orderSn);
            if (r.getCode() == 0){
                OrderVo orderVo = r.getData(new TypeReference<OrderVo>() {});
                if (orderVo== null ||orderVo.getStatus() == 4){
                    //TODO 类内方法互调 事务失效 原因: this.unlockStock this指向当前目标对象 解决办法:使用代理对象调 用ASPECTJ EXPOSE
                    unlockStock(taskDetail.getSkuId(),taskDetail.getWareId(),taskDetail.getSkuNum(),taskDetail.getId());
                }//在到了自动解锁库存时, 如果库存被锁定 订单却不是已关闭状态 很危险 应该nack 这里逻辑有缺陷
                //但加上关单时主动通知库存解锁,问题不大
                //再加上写程序检查不合理的订单与库存状态 等服务器闲时定时检查
            }else {
                throw new RuntimeException("远程服务失败");
            }
        }
    }

    @Transactional
    @Override
    public void unlockStock(OrderTo to) {
        WareOrderTaskEntity wareOrderTask = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", to.getOrderSn()));
        Long taskId = wareOrderTask.getId();
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", taskId).eq("lock_status", 1));
        if (list!=null){
            for (WareOrderTaskDetailEntity taskDetail : list) {
                unlockStock(taskDetail.getSkuId(),taskDetail.getWareId(),taskDetail.getSkuNum(),taskDetail.getId());
            }
        }
    }


    @Transactional
    public void unlockStock(Long skuId,Long wareId,Integer num,Long taskDetailId){
        wareSkuDao.unlockStock(skuId,wareId,num);
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(taskDetailId);
        wareOrderTaskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(wareOrderTaskDetailEntity);
    }
    @Data
    class SkuWareHasStock {
        private Long skuId;
        private Integer num;
        private List<Long> wareId;
    }
}